package xj.graph2d;

import static xj.util.xml.XMLUtil.getXMLAttributeString;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import xj.lang.CodeWriter;

public final class DrawAttr implements Cloneable, Serializable {

  public static final Color defaultPenColor = new Color(25, 25, 112); 
  //Color.black;

  public static final Color defaultFillColor = Color.lightGray;

  public static final float preferedPenSize = 1;

  public static final int preferedFontSize = 12;

  public static final int LINE_CAP_BUTT = BasicStroke.CAP_BUTT;
  public static final int LINE_CAP_ROUND = BasicStroke.CAP_ROUND;
  public static final int LINE_CAP_SQUARE = BasicStroke.CAP_SQUARE;
  public static final int LINE_JOIN_MITER = BasicStroke.JOIN_MITER;
  public static final int LINE_JOIN_ROUND = BasicStroke.JOIN_ROUND;
  public static final int LINE_JOIN_BEVEL = BasicStroke.JOIN_BEVEL;

  public static final int SOLID_LINE        = 0;
  public static final int DASHED_LINE       = 1;
  public static final int DASH_DOT_LINE     = 2;
  public static final int DASH_DOT_DOT_LINE = 3;
  public static final int DOTTED_LINE       = 4;

  public static final int DEFAULT_GRADIENT = 0;  
  public static final int LINEAR_GRADIENT_LEFT_TO_RIGHT = 0x1;
  public static final int LINEAR_GRADIENT_TOP_TO_BOTTOM = 0x2;
  public static final int LINEAR_GRADIENT_DIAGNAL_DOWN  = 0x3;
  public static final int LINEAR_GRADIENT_DIAGNAL_UP    = 0x4;

  public static final int RADIAL_GRADIENT_CENTER        = 0x10;
  public static final int RADIAL_GRADIENT_UPPER_LEFT    = 0x11;
  public static final int RADIAL_GRADIENT_UPPER_RIGHT   = 0x12;
  public static final int RADIAL_GRADIENT_LOWER_LEFT    = 0x13;
  public static final int RADIAL_GRADIENT_LOWER_RIGHT   = 0x14;

  protected static int defaultFont;

  protected static FontMap fontmap;

  static {
    fontmap = FontMap.getInstance();
    defaultFont = fontmap.findFont("SansSerif", Font.PLAIN, 12);
  }

  protected Color penColor = defaultPenColor;
  protected Color fillColor = defaultFillColor;

  protected Color gradientColor1 = null;
  protected Color gradientColor2 = null;
  protected int gradientMode = DEFAULT_GRADIENT;
  protected boolean gradientReflect = false; 

  protected int penSize = 1;

  protected int lineCap = LINE_CAP_BUTT;

  protected int lineJoin = LINE_JOIN_MITER;

  protected int lineStyle = SOLID_LINE;

  transient int font = defaultFont;

  protected boolean underlined = false;

  public DrawAttr() {
  }

  public DrawAttr(Color penColor) {
    this.penColor = penColor;
  }

  public DrawAttr(int font) {
    this.font = font;
  }

  public static void setDefaultFont(String name, int style, int size) {
    defaultFont = fontmap.findFont(name, style, size);
  }

  @Override
  public Object clone() {
    DrawAttr attr;
    try {
      attr = (DrawAttr) super.clone();
    } catch (CloneNotSupportedException e) {
      attr = new DrawAttr();
    }
    attr.penColor = penColor;
    attr.fillColor = fillColor;
    attr.gradientColor1 = gradientColor1;
    attr.gradientColor2 = gradientColor2;
    attr.gradientMode = gradientMode;
    attr.gradientReflect = gradientReflect;
    attr.penSize = penSize;
    attr.font = font;
    attr.underlined = underlined;
    attr.lineCap = lineCap;
    attr.lineJoin = lineJoin;
    attr.lineStyle = lineStyle;
    return attr;
  }

  public void copyDrawAttr(DrawAttr attr) {
    if (attr != null) {
      attr.penColor = penColor;
      attr.fillColor = fillColor;
      attr.gradientColor1 = gradientColor1;
      attr.gradientColor2 = gradientColor2;
      attr.gradientMode = gradientMode;
      attr.gradientReflect = gradientReflect;
      attr.penSize = penSize;
      attr.font = font;
      attr.underlined = underlined;
      attr.lineCap = lineCap;
      attr.lineJoin = lineJoin;
      attr.lineStyle = lineStyle;
    }
  }

  public void setPenColor(Color c) {
    penColor = c;
  }

  public Color getPenColor() {
    return penColor;
  }

  public void setFillColor(Color c) {
    fillColor = c;
  }

  public Color getFillColor() {
    return fillColor;
  }

  public Color getGradientColor1() {
    return gradientColor1;
  }

  public void setGradientColor1(Color c) {
    gradientColor1 = c;
  }

  public Color getGradientColor2() {
    return gradientColor2;
  }

  public void setGradientColor2(Color c) {
    gradientColor2 = c;
  }

  public int getGradientMode() { 
    return gradientMode;
  }

  public void setGradientMode(int g) { 
    gradientMode = g;
  }

  public boolean isLinearGradientMode() { 
    return (gradientMode > 0 &&  gradientMode < 0x10);
  }

  public boolean isRadialGradientMode() { 
    return (gradientMode >= 0x10);
  }

  public boolean isGradientReflect() { 
    return gradientReflect;
  }

  public void setGradientReflect(boolean r) { 
    gradientReflect = r;
  }

  public static Color getDefaultPenColor() {
    return defaultPenColor;
  }

  public static Color getDefaultFillColor() {
    return defaultFillColor;
  }

  public int getPenSize() {
    return penSize;
  }

  public void setPenSize(int penSize) {
    this.penSize = penSize;
  }

  public int getLineCap() {
    return lineCap;
  }

  public void setLineCap(int lineCap) {
    this.lineCap = lineCap;
  }

  public int getLineJoin() {
    return lineJoin;
  }

  public void setLineJoin(int lineJoin) {
    this.lineJoin = lineJoin;
  }

  public int getLineStyle() {
    return lineStyle;
  }

  public void setLineStyle(int lineStyle) {
    this.lineStyle = lineStyle;
  }

  public final void setFont(String name, int style, int size) {
    // FontAttr fontAttr = fontmap.getFontAttr(font);
    if (size < 2) {
      size = 2;
    }
    font = fontmap.findFont(name, style, size);
  }

  public final void setFontName(String name) {
    FontAttr fontAttr = fontmap.getFontAttr(font);
    if (name != null && !name.equals(fontAttr.fontName)) {
      font = fontmap.findFont(name, fontAttr.fontStyle, fontAttr.fontSize);
    }
  }

  public final void setFontStyle(int style) {
    FontAttr fontAttr = fontmap.getFontAttr(font);
    if (style != fontAttr.fontStyle) {
      font = fontmap.findFont(fontAttr.fontName, style, fontAttr.fontSize);
    }
  }

  public final void setFontSize(int size) {
    FontAttr fontAttr = fontmap.getFontAttr(font);
    if (size < 2) {
      size = 2;
    }
    if (size != fontAttr.fontSize) {
      font = fontmap.findFont(fontAttr.fontName, fontAttr.fontStyle, size);
    }
  }

  public final void scaleFontSize(float s) {
    FontAttr fontAttr = fontmap.getFontAttr(font);
    int size = Math.round(fontAttr.fontSize * s);
    if (size < 6) {
      size = 6;
    }
    if (size != fontAttr.fontSize) {
      font = fontmap.findFont(fontAttr.fontName, fontAttr.fontStyle, size);
    }
  }

  public void setBoldFont(boolean bold) {
    FontAttr fontAttr = fontmap.getFontAttr(font);
    boolean isBold = (fontAttr.fontStyle & Font.BOLD) != 0;
    if (isBold != bold) {
      int style;
      if (bold) {
	style = fontAttr.fontStyle | Font.BOLD;
      } else {
	style = fontAttr.fontStyle & ~Font.BOLD;
      }
      font = fontmap.findFont(fontAttr.fontName, style, fontAttr.fontSize);
    }
  }

  public void setItalicFont(boolean italic) {
    FontAttr fontAttr = fontmap.getFontAttr(font);
    boolean isItalic = (fontAttr.fontStyle & Font.ITALIC) != 0;
    if (isItalic != italic) {
      int style;
      if (italic) {
	style = fontAttr.fontStyle | Font.ITALIC;
      } else {
	style = fontAttr.fontStyle & ~Font.ITALIC;
      }
      font = fontmap.findFont(fontAttr.fontName, style, fontAttr.fontSize);
    }
  }

  public boolean isUnderlined() {
    return underlined;
  }

  public void setUnderlined(boolean underlined) {
    this.underlined = underlined;
  }

  public final void setFont(int font) {
    this.font = font;
  }

  public final int getFontId() {
    return font;
  }

  public final Font getFont() {
    return fontmap.getFont(font);
  }

  public final FontAttr getFontAttr() {
    return fontmap.getFontAttr(font);
  }

  public final FontMetrics getFontMetrics() {
    FontMap fontmap = FontMap.getInstance();
    return fontmap.getFontMetrics(font);
  }

  static final public FontMetrics getDefaultFontMetrics() {
    FontMap fontmap = FontMap.getInstance();
    return fontmap.getFontMetrics(defaultFont);
  }

  static final public Font getDefaultFont() {
    return fontmap.getFont(defaultFont);
  }

  static final public int getDefaultFontId() {
    return defaultFont;
  }

  @Override
  public String toString() {
    return ("[penColor=" + penColor + " fillColor=" + fillColor + " penSize=" + penSize +
	    // " penShape=" + penShape +
	    " font=" + font + "]");
  }

  public void writeXML(CodeWriter out) {
    writeXML(out, null);
  }

  public void writeXML(CodeWriter out, String namespacePrefix) {
    if (out != null) {
      writeXMLColor(out, penColor, "Color", namespacePrefix);
      writeXMLColor(out, fillColor, "FillColor", namespacePrefix);
      if (gradientColor1 != null) { 
	writeXMLColor(out, gradientColor1, "GradientColor1", namespacePrefix);
      }
      if (gradientColor2 != null) { 
	writeXMLColor(out, gradientColor2, "GradientColor2", namespacePrefix);
      }

      String nodename = DrawShapeBase.getPrefixedName("Gradient", namespacePrefix);
      out.println("<" + nodename + ' ' + 
		  getXMLAttributeString("mode", gradientMode) + ' ' + 
		  getXMLAttributeString("reflect", gradientReflect) + "/>");

      nodename = DrawShapeBase.getPrefixedName("Line", namespacePrefix);
      out.println("<" + nodename + ' ' + 
		  getXMLAttributeString("width", penSize) + ' ' + 
		  getXMLAttributeString("cap", lineCap) + ' ' + 
		  getXMLAttributeString("join", lineJoin) + ' ' + 
		  getXMLAttributeString("style", lineStyle) + "/>");
    }
  }

  protected void writeXMLColor(CodeWriter out, Color c, String elementName,
			       String namespacePrefix) {
    if (c != null) {
      String nodename = DrawShapeBase.getPrefixedName(elementName, namespacePrefix);
      out.println("<" + nodename + ' ' + getXMLAttributeString("r", c.getRed()) + ' ' + 
		  getXMLAttributeString("g", c.getGreen()) + ' ' + 
		  getXMLAttributeString("b", c.getBlue()) + ' ' + 
		  getXMLAttributeString("a", c.getAlpha()) + "/>");
    }
  }

  public void writeXMLFont(CodeWriter out) {
    writeXMLFont(out, null);
  }

  public void writeXMLFont(CodeWriter out, String namespacePrefix) {
    if (out != null) {
      FontMap fontmap = FontMap.getInstance();
      FontAttr fa = fontmap.getFontAttr(font);
      String nodename = DrawShapeBase.getPrefixedName("Font", namespacePrefix);
      out.println("<" + nodename + ' ' + 
		  getXMLAttributeString("name", fa.fontName) + ' ' + 
		  getXMLAttributeString("size", fa.fontSize) + ' ' + 
		  getXMLAttributeString("style", fa.fontStyle) + ' ' + 
		  getXMLAttributeString("underlined", underlined) + "/>");
    }
  }

  public void setAttrFromXML(org.w3c.dom.Element e) {
    setAttrFromXML(e, null);
  }

  public void setAttrFromXML(org.w3c.dom.Element e, String namespaceURI) {
    if (e != null) {
      String name = e.getLocalName();
      String uri = e.getNamespaceURI();
      if (namespaceURI == null || namespaceURI.equals(uri)) {
	if ("Color".equals(name)) {
	  if (debug) {
	    System.out.println("DrawAttr.setAttrFromXML(): <Color> tag");
	  }
	  setPenColor(readColorFromXML(e));
	} else if ("FillColor".equals(name)) {
	  if (debug) {
	    System.out.println("DrawAttr.setAttrFromXML(): <FillColor> tag");
	  }
	  setFillColor(readColorFromXML(e));
	} else if ("GradientColor1".equals(name)) {
	  if (debug) {
	    System.out.println("DrawAttr.setAttrFromXML(): <GradientColor1> tag");
	  }
	  setGradientColor1(readColorFromXML(e));
	} else if ("GradientColor2".equals(name)) {
	  if (debug) {
	    System.out.println("DrawAttr.setAttrFromXML(): <GradientColor2> tag");
	  }
	  setGradientColor2(readColorFromXML(e));
	} else if ("Gradient".equals(name)) {
	  if (debug) {
	    System.out.println("DrawAttr.setAttrFromXML(): <Gradient> tag");
	  }
	  String ms, fs;
	  ms = e.getAttribute("mode");
	  fs = e.getAttribute("reflect");
	  try {
	    int m = Integer.parseInt(ms);
	    setGradientMode(m);
	  } catch (NumberFormatException ex) {
	  }
	  setGradientReflect(Boolean.valueOf(fs));
	} else if ("Line".equals(name)) {
	  if (debug) {
	    System.out.println("DrawAttr.setAttrFromXML(): <Line> tag");
	  }
	  String ws, cs, js, ss;
	  int w, c, j, s;
	  ws = e.getAttribute("width");
	  cs = e.getAttribute("cap");
	  js = e.getAttribute("join");
	  ss = e.getAttribute("style");
	  try {
	    w = Integer.parseInt(ws);
	    c = Integer.parseInt(cs);
	    j = Integer.parseInt(js);
	    s = Integer.parseInt(ss);
	    setPenSize(w);
	    setLineCap(c);
	    setLineJoin(j);
	    setLineStyle(s);
	  } catch (NumberFormatException ex) {
	  }
	} else if ("Font".equals(name)) {
	  if (debug) {
	    System.out.println("DrawAttr.setAttrFromXML(): <Font> tag");
	  }
	  String fontName, fontSizeStr, fontStyleStr, fontUnderlinedStr;
	  int fontSize, fontStyle;
	  fontName = e.getAttribute("name");
	  fontSizeStr = e.getAttribute("size");
	  fontStyleStr = e.getAttribute("style");
	  fontUnderlinedStr = e.getAttribute("underlined");
	  try {
	    fontSize = Integer.parseInt(fontSizeStr);
	    fontStyle = Integer.parseInt(fontStyleStr);
	    setFont(fontName, fontStyle, fontSize);
	    underlined = Boolean.valueOf(fontUnderlinedStr).booleanValue();
	  } catch (NumberFormatException ex) {
	  }
	}
      }
    }
  }

  protected Color readColorFromXML(org.w3c.dom.Element e) {
    if (e != null) {
      String rs, gs, bs, as;
      int r, g, b, a;
      rs = e.getAttribute("r");
      gs = e.getAttribute("g");
      bs = e.getAttribute("b");
      as = e.getAttribute("a");
      try {
	r = Integer.parseInt(rs);
	g = Integer.parseInt(gs);
	b = Integer.parseInt(bs);
	a = Integer.parseInt(as);
	return new Color(r, g, b, a);
      } catch (NumberFormatException ex) {
      }
    }
    return null;
  }

  private void writeObject(ObjectOutputStream out) throws IOException {
    out.defaultWriteObject();
    FontMap fontmap = FontMap.getInstance();
    FontAttr fa = fontmap.getFontAttr(font);
    out.writeObject(fa);
  }

  private void readObject(ObjectInputStream in) throws IOException,
      ClassNotFoundException {
    in.defaultReadObject();
    Object obj = in.readObject();
    if (obj instanceof FontAttr) {
      FontAttr fa = (FontAttr) obj;
      FontMap fontmap = FontMap.getInstance();
      font = fontmap.findFont(fa);
    }
  }

  protected static final boolean debug = false;

}
